package com.tn.rtdata;

import java.io.Serializable;
import java.util.Date;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

/**
 * A bean used to record stats about event processing times.
 */
public class DataEventStats implements Serializable
{
  private Date created;
  private String dataEventId;
  private DataEvent.Type dataEventType;
  private String fillName;
  private int processed;
  private Long processingTimeAverage;
  private Long processingTimeMinimum;
  private Long processingTimeMaximum;

  /**
   * Creates a new <code>DataEventStats</code> initialized with the <code>dataEventId</code> and the <code>dataEventType</code>.
   */
  public DataEventStats(String dataEventId, String fillName, DataEvent.Type dataEventType)
  {
    this.dataEventId = dataEventId;
    this.fillName = fillName;
    this.dataEventType = dataEventType;
    this.created = new Date();
  }

  /**
   * Returns the created timestamp.
   */
  public Date getCreated()
  {
    return created;
  }

  /**
   * Returns the data event id of the data event these stats relate to.
   */
  public String getDataEventId()
  {
    return dataEventId;
  }

  /**
   * Returns the type of data event these stats relate to.
   */
  public DataEvent.Type getDataEventType()
  {
    return dataEventType;
  }

  /**
   * Returns the fill name.
   */
  public String getFillName()
  {
    return fillName;
  }

  /**
   * Returns the number of times the event was processed.
   */
  public int getProcessed()
  {
    return processed;
  }

  /**
   * Returns the average processing time.
   */
  public Long getProcessingTimeAverage()
  {
    return processingTimeAverage;
  }

  /**
   * Returns the minimum processing time.
   */
  public Long getProcessingTimeMinimum()
  {
    return processingTimeMinimum;
  }

  /**
   * Returns the maximum processing time.
   */
  public Long getProcessingTimeMaximum()
  {
    return processingTimeMaximum;
  }

  /**
   * Creates a new <code>DataEventStats</code> based on an event being processed.
   */
  public DataEventStats processed(Date at)
  {
    DataEventStats dataEventStats = new DataEventStats(dataEventId, fillName, dataEventType);
    dataEventStats.created = created;

    long processingTime = at.getTime() - created.getTime();

    if (DataEvent.Type.INITIAL.equals(dataEventType))
    {
      dataEventStats.processed = 1;
      dataEventStats.processingTimeAverage = processingTime;
      dataEventStats.processingTimeMinimum = processingTime;
      dataEventStats.processingTimeMaximum = processingTime;
    }
    else
    {
      dataEventStats.processed = processed + 1;

      if (processed == 0)
      {
        dataEventStats.processingTimeAverage = processingTime;
        dataEventStats.processingTimeMinimum = processingTime;
        dataEventStats.processingTimeMaximum = processingTime;
      }
      else
      {
        dataEventStats.processingTimeAverage = ((processingTimeAverage * processed) + processingTime) / (processed + 1);
        dataEventStats.processingTimeMinimum = processingTimeMinimum < processingTime ? processingTimeMinimum : processingTime;
        dataEventStats.processingTimeMaximum = processingTimeMaximum > processingTime ? processingTimeMaximum : processingTime;
      }
    }

    return dataEventStats;
  }

  /**
   * Compares this object to another for equality.  For another object to be considered equal to a <code>DataEventStats</code> the other
   * object must be a <code>DataEventStats</code> and must have been initialized with the same values.
   *
   * @param other the other object.
   *
   * @return <code>true</code> if the objects are considered equal, otherwise <code>false</code>.
   */
  @Override
  public boolean equals(Object other)
  {
    return this == other || other != null && getClass() == other.getClass() &&
      new EqualsBuilder()
        .append(getDataEventId(), ((DataEventStats)other).getDataEventId())
        .append(getFillName(), ((DataEventStats)other).getFillName())
        .append(getDataEventType(), ((DataEventStats) other).getDataEventType())
        .append(getCreated(), ((DataEventStats) other).getCreated())
        .append(getProcessed(), ((DataEventStats) other).getProcessed())
        .append(getProcessingTimeAverage(), ((DataEventStats) other).getProcessingTimeAverage())
        .append(getProcessingTimeMinimum(), ((DataEventStats) other).getProcessingTimeMinimum())
        .append(getProcessingTimeMaximum(), ((DataEventStats) other).getProcessingTimeMaximum())
        .isEquals();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int hashCode()
  {
    return new HashCodeBuilder()
      .append(getDataEventId())
      .append(getFillName())
      .append(getDataEventType())
      .append(getCreated())
      .append(getProcessed())
      .append(getProcessingTimeAverage())
      .append(getProcessingTimeMinimum())
      .append(getProcessingTimeMaximum())
      .toHashCode();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String toString()
  {
    return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
      .append("dataEventId", getDataEventId())
      .append("fillName", getFillName())
      .append("dataEventType", getDataEventType())
      .append("created", getCreated())
      .append("processed", getProcessed())
      .append("processingTimeAverage", getProcessingTimeAverage())
      .append("processingTimeMinimum", getProcessingTimeMinimum())
      .append("processingTimeMaximum", getProcessingTimeMaximum())
      .toString();
  }
}
